﻿using SnakeGame.Properties;
using System;
using System.Drawing;
using System.Media;
using System.Windows.Forms;

namespace SnakeGame {
    // 游戏区域
    class Game {
        // 回调
        public interface Callback {
            void OnDifficultyUp(int difficulty);    // 难度增加事件
            void OnGameOver(bool isWon);            // 游戏结束事件
            void OnScore(int score);                // 得分事件
        }

        // 状态
        public enum Status {
            END,    // 已结束
            PAUSED, // 暂停中
            PLAYING // 游戏中
        }

        public Status CurrentStatus { get; private set; }   // 当前状态
        private SnakeBody mApple;                           // 果子
        private readonly Brush mBrush;                      // 画刷
        private readonly Callback mCallback;                // 回调
        private int mDifficulty;                            // 难度
        private readonly Graphics mDrawer;                  // 绘图者
        private bool mEnableMusic;                          // 音乐开关
        private readonly Control mGameBoard;                // 游戏区域
        private readonly SoundPlayer mMusicPlayer;          // 音乐播放者
        private readonly Random mRandom;                    // 随机数生成器
        private int mScore;                                 // 得分
        private Snake mSnake;                               // 蛇
        private readonly Timer mTimer;                      // 定时器

        /**
         * 构造方法。
         * @param callback  回调
         * @param drawer    绘图者
         * @param gameBoard 游戏区域
         */
        public Game(Callback callback, Graphics drawer, Control gameBoard) {
            // 初始化成员
            CurrentStatus = Status.END;
            mBrush = new SolidBrush(Const.GAME_BOARD_COLOR);
            mCallback = callback;
            mDrawer = drawer;
            mDifficulty = 1;
            mEnableMusic = false;
            mGameBoard = gameBoard;
            mMusicPlayer = new SoundPlayer(Resources.music);
            mRandom = new Random();
            mScore = 0;
            mTimer = new Timer();
            mTimer.Tick += new EventHandler(OnTimer);
            mTimer.Interval = Const.TIMER_INIT;

            // 重绘游戏区域
            this.DrawGameBoard();
        }

        /**
         * 在随机位置创建果子。
         */
        private void CreateApple() {
            Point point = new Point();
            bool used = false;

            // 创建果子
            do {
                // 取 x、y 的随机值
                point.X = mRandom.Next(1, Const.GAME_BOARD_GRID_COUNT_H - 1);
                point.Y = mRandom.Next(1, Const.GAME_BOARD_GRID_COUNT_V - 1);
                used = this.PointIsUsed(point);
            } while (used);
            mApple = new SnakeBody(mDrawer, point, SnakeBody.Type.APPLE);
        }

        /**
         * 画游戏区域。
         */
        private void DrawGameBoard() {
            mDrawer.FillRectangle(mBrush, 0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT);
        }

        /**
         * 游戏结束。
         * @param isWon  胜利则为 true，否则为 false
         */
        private void GameOver(bool isWon) {
            // 将状态设置为已结束
            CurrentStatus = Status.END;

            // 停止定时器
            mTimer.Stop();

            // 停止播放音乐
            if (mEnableMusic) {
                mMusicPlayer.Stop();
            }

            // 发送通知
            mCallback.OnGameOver(isWon);
        }

        /**
         * 判断是否胜利。
         * @return 胜利则返回 true，否则返回 false
         */
        private bool IsWon() {
            return mScore >= Const.WIN_NUMBER;
        }

        /**
         * 按键事件的响应方法。
         * @param key   按键
         * @return 事件在此被处理则返回 true，否则返回 false
         */
        public bool OnKeyPressed(Keys key) {
            if (CurrentStatus != Status.PLAYING) {
                return false;
            }
            if (key >= Keys.Left && key <= Keys.Down) {
                mSnake.Turn(key);
                return true;
            }
            return false;
        }

        /*
         * 定时器事件的响应方法。
         * @param sender    事件发送者
         * @param e         事件相关信息
         */
        private void OnTimer(object sender, EventArgs e) {
            // 移动蛇
            bool isDead = mSnake.Move();
            if (isDead) {
                this.GameOver(false);
                return;
            }

            // 检查是否碰到果子
            SnakeBody head = mSnake.Head;
            if (head.Position.Equals(mApple.Position)) {
                // 碰到果子，将果子加入蛇链表
                mSnake.Eat(mApple);
                this.CreateApple();
                mScore++;
                mCallback.OnScore(mScore);

                // 判断是否胜利
                if (this.IsWon()) {
                    this.GameOver(true);
                    return;
                }

                // 判断是否达到增加难度的条件
                if (mScore % Const.DIFF_BASE == 0 && mDifficulty != Const.DIFF_MAX) {
                    mDifficulty++;
                    mCallback.OnDifficultyUp(mDifficulty);
                    mTimer.Stop();
                    mTimer.Interval = Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1);
                    mTimer.Start();
                }
            }

            // 通知游戏区域重绘
            mGameBoard.Invalidate(false);
        }

        /**
         * 游戏暂停。
         */
        public void Pause() {
            if (CurrentStatus == Status.PLAYING) {
                // 停止定时器
                mTimer.Stop();

                // 停止播放音乐
                if (mEnableMusic) {
                    mMusicPlayer.Stop();
                }

                // 将状态设置为已暂停
                CurrentStatus = Status.PAUSED;
            } else if (CurrentStatus == Status.PAUSED) {
                // 开启定时器
                mTimer.Start();

                // 播放音乐
                if (mEnableMusic) {
                    mMusicPlayer.PlayLooping();
                }

                // 将状态设置为游戏中
                CurrentStatus = Status.PLAYING;
            }
        }

        /**
         * 播放音乐。
         * @return 正在播放音乐则为 true，否则为 false
         */
        public bool PlayMusic() {
            mEnableMusic = !mEnableMusic;

            if (mEnableMusic) {
                if (CurrentStatus == Status.PLAYING) {
                    mMusicPlayer.PlayLooping();
                }
            } else {
                mMusicPlayer.Stop();
            }

            return mEnableMusic;
        }

        /**
         * 判断某个点是否被占用。
         * @param point 点
         * @return 被占用了则返回 true，否则返回 false
         */
        private bool PointIsUsed(Point point) {
            // 检查点是否在蛇上
            if (mSnake != null) {
                if (mSnake.PointIsOnMe(point)) {
                    return true;
                }
            }

            // 检查点是否在果子上
            if (mApple != null) {
                return mApple.Position.Equals(point);
            }

            return false;
        }
        
        /**
         * 游戏开始。
         */
        public void Start() {
            // 重绘游戏区域
            this.DrawGameBoard();

            // 创建蛇
            mSnake = new Snake(mDrawer);

            // 创建果子
            this.CreateApple();

            // 通知游戏区域重绘
            mGameBoard.Invalidate(false);

            // 重置难度
            mDifficulty = 1;

            // 重置分数
            mScore = 0;

            // 开启定时器
            mTimer.Stop();
            mTimer.Interval = Const.TIMER_INIT;
            mTimer.Start();

            // 播放音乐
            if (mEnableMusic) {
                mMusicPlayer.PlayLooping();
            }

            // 将状态设置为游戏中
            CurrentStatus = Status.PLAYING;
        }
    }
}
